Fix native fullscreen unreachable on the main window (#5933) - #6830
Conversation
cmux creates its main window programmatically and never declares .fullScreenPrimary, relying on AppKit's implicit grant of fullscreen capability to a resizable, titled window. On macOS 26 (Tahoe) a freshly-created CmuxMainWindow reports an empty collection behavior (rawValue == 0) and AppKit does not treat it as fullscreen-capable, so Toggle Full Screen / ⌃⌘F / the green traffic-light button all fail to enter a native fullscreen Space (the green button only zooms). This test asserts the main window declares .fullScreenPrimary. It fails on current code (no fix yet) and will pass once the window declares the capability explicitly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes #5933. CmuxMainWindow is created programmatically and never declared `.fullScreenPrimary`, so it relied on AppKit implicitly granting fullscreen capability to a resizable, titled window. On macOS 26 (Tahoe) that implicit grant does not happen — a freshly-created window reports an empty collection behavior (`rawValue == 0`) and AppKit does not treat it as fullscreen-capable. As a result Toggle Full Screen, ⌃⌘F, and the green traffic-light button all fail to enter a native fullscreen Space (the green button only zooms), which is what multi-monitor Tahoe users hit. Declare `.fullScreenPrimary` explicitly in the window initializer via a pure, unit-testable `canonicalCollectionBehavior(_:)` helper so native fullscreen is reachable regardless of the OS's implicit default. The helper also strips any stale `.fullScreenNone` and preserves unrelated bits, so it composes with the temporary `.fullScreenDisallowsTiling` opt-out the window factory applies when spawning a window out of an existing fullscreen Space. This makes the regression test added in the previous commit pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
More reviews will be available in 39 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR fixes native fullscreen being unreachable on the
Confidence Score: 5/5Safe to merge — the change is a minimal, focused init override with no observable side effects on setups where AppKit already granted fullscreen capability. The production change is a single idempotent write to No files require special attention. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["CmuxMainWindow.init(contentRect:styleMask:backing:defer:)"]
B["super.init → AppKit sets collectionBehavior\n(rawValue == 0 on macOS 26 Tahoe)"]
C["canonicalCollectionBehavior(collectionBehavior)"]
D{"contains .fullScreenNone?"}
E["remove(.fullScreenNone)"]
F["insert(.fullScreenPrimary)"]
G["return updated CollectionBehavior"]
H["self.collectionBehavior = result\n(preserves any other bits already set)"]
I["Window factory may later insert\n.fullScreenDisallowsTiling — preserved\nbecause insert is additive"]
A --> B
B --> C
C --> D
D -- yes --> E --> F
D -- no --> F
F --> G --> H --> I
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["CmuxMainWindow.init(contentRect:styleMask:backing:defer:)"]
B["super.init → AppKit sets collectionBehavior\n(rawValue == 0 on macOS 26 Tahoe)"]
C["canonicalCollectionBehavior(collectionBehavior)"]
D{"contains .fullScreenNone?"}
E["remove(.fullScreenNone)"]
F["insert(.fullScreenPrimary)"]
G["return updated CollectionBehavior"]
H["self.collectionBehavior = result\n(preserves any other bits already set)"]
I["Window factory may later insert\n.fullScreenDisallowsTiling — preserved\nbecause insert is additive"]
A --> B
B --> C
C --> D
D -- yes --> E --> F
D -- no --> F
F --> G --> H --> I
Reviews (2): Last reviewed commit: "Use Swift Testing for the fullscreen cap..." | Re-trigger Greptile |
Aziz test-framework policy: new, non-UI test files should use Swift Testing (XCTest stays for cmuxUITests only). Convert the new CmuxMainWindowFullScreenCapabilityTests from XCTestCase/XCTAssert to a @mainactor @suite with @Test/#expect. No behavior change — same window-instantiation assertion plus the four pure-helper contracts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes #5933.
Problem
On macOS 26 (Tahoe), cmux's main window cannot enter native fullscreen by any route:
All three entrypoints resolve the right
CmuxMainWindowand calltoggleFullScreen(_:), so window targeting is fine. The symptom set (toggle is a no-op and the green button zooms instead of fullscreening) is the textbook signature of a window that is not fullscreen-capable.Root cause
CmuxMainWindowis created programmatically (never from a nib), so it cannot inherit fullscreen capability from Interface Builder. It never declared.fullScreenPrimaryand relied on AppKit implicitly granting fullscreen to a resizable, titled window.That implicit grant is not reliable across macOS versions. Verified empirically on macOS 26.5 (Tahoe): a window created with cmux's exact style mask (
[.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView]) reportscollectionBehavior.rawValue == 0— i.e. it carries none of.fullScreenPrimary/.fullScreenAuxiliary/.fullScreenNone, and AppKit does not treat it as fullscreen-capable. This matches CLAUDE.md's standing warning that AppKit semantics change silently between macOS majors, so implicit defaults must not be assumed stable.Fix
Declare
.fullScreenPrimaryexplicitly inCmuxMainWindow's initializer, via a pure, unit-testablecanonicalCollectionBehavior(_:)helper. The helper:.fullScreenPrimaryso native fullscreen is always reachable,.fullScreenNone(mutually exclusive with primary),.fullScreenDisallowsTilingopt-out the window factory layers on when spawning a window out of an existing fullscreen Space.It is idempotent and a no-op where AppKit would have granted fullscreen anyway, so it cannot regress setups that already worked.
Tests
Two-commit red/green:
CmuxMainWindowFullScreenCapabilityTests(wired intoproject.pbxproj). The window-instantiation test fails on current code because the default collection behavior lacks.fullScreenPrimary.canonicalCollectionBehavior(_:)contract (adds primary, drops stale none, preserves unrelated bits, idempotent).Scope note: sleep/wake size shrink
The issue also mentions the window shrinking after display sleep/wake. The reporter is on v0.64.14; the sleep/wake reposition fix (#6305,
CmuxMainWindow.constrainFrameRect) landed in v0.64.17 / nightly and is already onmain, which lines up with the reporter's note that the bug "does not reproduce on NIGHTLY." This PR targets the fullscreen-capability defect, which is the part still present onmainand is not addressed anywhere else. The fullscreen change is orthogonal to and composes with the existing constrain-frame logic.Localization
No user-facing strings added or changed — the "Toggle Full Screen" command and its
command.toggleFullScreen.*strings already exist inLocalizable.xcstrings. Change is window-capability code + tests + pbxproj wiring only.🤖 Generated with Claude Code
Need help on this PR? Tag
/codesmithwith what you need. Autofix is disabled.Summary by cubic
Make the main window explicitly fullscreen-capable so native fullscreen works again via Toggle Full Screen, ⌃⌘F, and the green button on macOS 26 and multi-monitor setups. Fixes #5933.
canonicalCollectionBehavior(_:)and apply it inCmuxMainWindowinit to insert.fullScreenPrimary, remove.fullScreenNone, and preserve other behavior bits.@Suite,@Test).Written for commit 4199e20. Summary will update on new commits.